// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/test/mock_leveldb_database.h"

#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"

namespace content {

namespace {

    leveldb::mojom::KeyValuePtr CreateKeyValue(std::vector<uint8_t> key,
        std::vector<uint8_t> value)
    {
        leveldb::mojom::KeyValuePtr result = leveldb::mojom::KeyValue::New();
        result->key = std::move(key);
        result->value = std::move(value);
        return result;
    }

    base::StringPiece AsStringPiece(const std::vector<uint8_t>& data)
    {
        return base::StringPiece(reinterpret_cast<const char*>(data.data()),
            data.size());
    }

    bool StartsWith(const std::vector<uint8_t>& key,
        const std::vector<uint8_t>& prefix)
    {
        return base::StartsWith(AsStringPiece(key), AsStringPiece(prefix),
            base::CompareCase::SENSITIVE);
    }

    std::vector<uint8_t> successor(std::vector<uint8_t> data)
    {
        for (unsigned i = data.size(); i > 0; --i) {
            if (data[i - 1] < 255) {
                data[i - 1]++;
                return data;
            }
        }
        NOTREACHED();
        return data;
    }

} // namespace

MockLevelDBDatabase::MockLevelDBDatabase(
    std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data)
    : mock_data_(*mock_data)
{
}

void MockLevelDBDatabase::Put(const std::vector<uint8_t>& key,
    const std::vector<uint8_t>& value,
    const PutCallback& callback)
{
    mock_data_[key] = value;
    callback.Run(leveldb::mojom::DatabaseError::OK);
}

void MockLevelDBDatabase::Delete(const std::vector<uint8_t>& key,
    const DeleteCallback& callback)
{
    mock_data_.erase(key);
    callback.Run(leveldb::mojom::DatabaseError::OK);
}

void MockLevelDBDatabase::DeletePrefixed(
    const std::vector<uint8_t>& key_prefix,
    const DeletePrefixedCallback& callback)
{
    mock_data_.erase(mock_data_.lower_bound(key_prefix),
        mock_data_.lower_bound(successor(key_prefix)));
    callback.Run(leveldb::mojom::DatabaseError::OK);
}

void MockLevelDBDatabase::Write(
    std::vector<leveldb::mojom::BatchedOperationPtr> operations,
    const WriteCallback& callback)
{
    for (const auto& op : operations) {
        switch (op->type) {
        case leveldb::mojom::BatchOperationType::PUT_KEY:
            mock_data_[op->key] = *op->value;
            break;
        case leveldb::mojom::BatchOperationType::DELETE_KEY:
            mock_data_.erase(op->key);
            break;
        case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY:
            mock_data_.erase(mock_data_.lower_bound(op->key),
                mock_data_.lower_bound(successor(op->key)));
            break;
        }
    }
    callback.Run(leveldb::mojom::DatabaseError::OK);
}

void MockLevelDBDatabase::Get(const std::vector<uint8_t>& key,
    const GetCallback& callback)
{
    if (mock_data_.find(key) != mock_data_.end()) {
        callback.Run(leveldb::mojom::DatabaseError::OK, mock_data_[key]);
    } else {
        callback.Run(leveldb::mojom::DatabaseError::NOT_FOUND,
            std::vector<uint8_t>());
    }
}

void MockLevelDBDatabase::GetPrefixed(
    const std::vector<uint8_t>& key_prefix,
    const GetPrefixedCallback& callback)
{
    std::vector<leveldb::mojom::KeyValuePtr> data;
    for (const auto& row : mock_data_) {
        if (StartsWith(row.first, key_prefix)) {
            data.push_back(CreateKeyValue(row.first, row.second));
        }
    }
    callback.Run(leveldb::mojom::DatabaseError::OK, std::move(data));
}

void MockLevelDBDatabase::GetSnapshot(
    const GetSnapshotCallback& callback)
{
    NOTREACHED();
}

void MockLevelDBDatabase::ReleaseSnapshot(
    const base::UnguessableToken& snapshot)
{
    NOTREACHED();
}

void MockLevelDBDatabase::GetFromSnapshot(
    const base::UnguessableToken& snapshot,
    const std::vector<uint8_t>& key,
    const GetCallback& callback)
{
    NOTREACHED();
}

void MockLevelDBDatabase::NewIterator(
    const NewIteratorCallback& callback)
{
    NOTREACHED();
}

void MockLevelDBDatabase::NewIteratorFromSnapshot(
    const base::UnguessableToken& snapshot,
    const NewIteratorFromSnapshotCallback& callback)
{
    NOTREACHED();
}

void MockLevelDBDatabase::ReleaseIterator(
    const base::UnguessableToken& iterator)
{
    NOTREACHED();
}

void MockLevelDBDatabase::IteratorSeekToFirst(
    const base::UnguessableToken& iterator,
    const IteratorSeekToFirstCallback& callback)
{
    NOTREACHED();
}

void MockLevelDBDatabase::IteratorSeekToLast(
    const base::UnguessableToken& iterator,
    const IteratorSeekToLastCallback& callback)
{
    NOTREACHED();
}

void MockLevelDBDatabase::IteratorSeek(
    const base::UnguessableToken& iterator,
    const std::vector<uint8_t>& target,
    const IteratorSeekToLastCallback& callback)
{
    NOTREACHED();
}

void MockLevelDBDatabase::IteratorNext(
    const base::UnguessableToken& iterator,
    const IteratorNextCallback& callback)
{
    NOTREACHED();
}

void MockLevelDBDatabase::IteratorPrev(
    const base::UnguessableToken& iterator,
    const IteratorPrevCallback& callback)
{
    NOTREACHED();
}

} // namespace content
